﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace FileSplitter
{
    public class Splitter
    {
        public SplitterState CurSplitState { get; set; }
        public string ModelName { get; set; }
        public Action<float> NotifyProgress { get; set; }
        public Action<float> NotifyCombinePro { get; set; }
        public Dictionary<SplitterType, SplitterState> SplitterStateDic { get; set; }
        public float MaxFileSize { get; set; }
        public SaveParam SaveInfo { get; protected set; }
        /// <summary>
        /// 方法参数信息
        /// </summary>
        public List<object> StartSpliteFileParam { get; private set; }

        public Splitter()
        {
            InitStateDic();
            this.MaxFileSize = 5;
            this.ModelName = string.Empty;
            CurSplitState = SplitterStateDic[SplitterType.FilePathState];
        }

        private void InitStateDic()
        {
            this.SplitterStateDic = new Dictionary<SplitterType, SplitterState>();
            SplitterStateDic.Add(SplitterType.FilePathState, new SplitterStatePath());
            SplitterStateDic.Add(SplitterType.MomoryState, new SplitterStateMemory());
            StartSpliteFileParam = new List<object>();
        }

        public void AddNotifyProgress(Action<float> actionNotify)
        {
            this.NotifyProgress += actionNotify;
        }

        /// <summary>
        /// 设置成功保存信息，然后在分割文件
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public bool SetSaveInfo(SaveParam param)
        {
            if (!Directory.Exists(param.FloderDir) || !param.FileSuffix.StartsWith(".") || string.IsNullOrEmpty(param.FileName))
            {
                return false;
            }
            param.VaildFlag = true;
            this.SaveInfo = param;
            return true;
        }

        #region 分割文件
        public bool StartSpliteFile(string filePath, uint spliteNu)
        {
            if (!SaveInfo.VaildFlag || !File.Exists(filePath))
            {
                return false;
            }
            bool spliteReslut = false;
            lock (ModelName)
            {
                CheckCurSate(SplitterType.FilePathState);
                StartSpliteFileParam.Clear();
                StartSpliteFileParam.Add(filePath);
                StartSpliteFileParam.Add((int)spliteNu);
                try
                {
                    spliteReslut = CurSplitState.SplitFile(this);
                }
                catch (Exception)
                {}
            }
            return spliteReslut;
        }

        public bool StartSpliteFile(byte[] buffer, uint spliteNu)
        {
            if (!SaveInfo.VaildFlag || !OverLoadMaxFileSize(buffer.Length))
            {
                return false;
            }
            bool spliteReslut = false;
            lock (ModelName)
            {
                var curType = SplitterType.MomoryState;
                if (CurSplitState.CurType != curType)
                {
                    CurSplitState = this.SplitterStateDic[curType];
                }
                StartSpliteFileParam.Clear();
                StartSpliteFileParam.Add(buffer);
                StartSpliteFileParam.Add((int)spliteNu);
                try
                {
                    spliteReslut = CurSplitState.SplitFile(this);
                }
                catch (Exception)
                { }

            }
            return spliteReslut;
        }


        internal bool OverLoadMaxFileSize(float bytesNumber)
        {
            float fileSizeM = bytesNumber / 1024 / 1024;
            return fileSizeM > MaxFileSize;
        }
        #endregion

        #region 合并文件

        public bool CombineFiles(List<string> pathList)
        {
            if (!SaveInfo.VaildFlag)
            {
                return false;
            }
            bool ret = false;
            lock (ModelName)
            {
                CheckCurSate(SplitterType.FilePathState);
                StartSpliteFileParam.Clear();
                StartSpliteFileParam.Add(pathList);
                ret = CurSplitState.CombineFile(this);
            }
            return ret;
        }

        public bool CombineFiles(List<byte[]> bytesLs)
        {
            if (!SaveInfo.VaildFlag)
            {
                return false;
            }
            bool ret = false;
            lock (ModelName)
            {
                CheckCurSate(SplitterType.MomoryState);
                StartSpliteFileParam.Clear();
                StartSpliteFileParam.Add(bytesLs);
                ret = CurSplitState.CombineFile(this);
            }
            return ret;
        }
        #endregion

        internal bool SaveFile(byte[] buf, int length, int index)
        {
            string saveFilePath = this.SaveInfo.CompoundPath(index.ToString());
            if (string.IsNullOrEmpty(saveFilePath) || length > buf.Length)
            {
                return false;
            }
            try
            {
                if (buf.Length == length)
                {
                    File.WriteAllBytes(saveFilePath, buf);
                }
                else
                {
                    byte[] bts = new byte[length];
                    Array.Copy(buf, 0, bts, 0, bts.Length);
                    File.WriteAllBytes(saveFilePath, bts);
                }
            }
            catch (Exception)
            {

                return false;
            }
            return true;
        }

        //追加到已经存在数据后边
        internal bool SaveFile(byte[] buf)
        {
            string saveFilePath = this.SaveInfo.CompoundPath(string.Empty);
            if (string.IsNullOrEmpty(saveFilePath))
            {
                return false;
            }
            if (!File.Exists(saveFilePath))
            {
                using (File.Create(saveFilePath))
                {
                }
            }
            using (FileStream fs = new FileStream(saveFilePath, FileMode.Append, FileAccess.Write))
            {
                try
                {
                    fs.Write(buf, 0, buf.Length);
                }
                catch (Exception)
                {

                    return false;
                }

            }
            return true;
        }

        void CheckCurSate(SplitterType type)
        {
            if (type != CurSplitState.CurType && this.SplitterStateDic.ContainsKey(type))
            {
                CurSplitState = SplitterStateDic[type];
            }
        }

        /// <summary>
        /// 通知订阅对象当前进度
        /// </summary>
        /// <param name="f">当前进度</param>
        /// <param name="notityType">1通知分割进度，2通知合并进度</param>
        public void NotifyEvent(float f, int notityType)
        {
            if (notityType == 1)
            {
                if (this.NotifyProgress != null)
                {
                    NotifyProgress(f);
                }
            }
            else if (notityType == 2)
            {
                if (NotifyCombinePro != null)
                {
                    NotifyCombinePro(f);
                }
            }
        }
    }

    public abstract class SplitterState
    {
        public virtual SplitterType CurType { get; }
        public float CurrentProgress { get; protected set; }
        public abstract bool SplitFile(Splitter splitter);
        public abstract bool CombineFile(Splitter splitter);

        protected virtual bool CommonSpliteRAS(int loopNu, Stream fs, Splitter sp)
        {
            if (fs == null || !sp.OverLoadMaxFileSize(fs.Length))
            {
                return false;
            }
            CurrentProgress = 0f;
            sp.NotifyEvent(CurrentProgress, 1);
            //1.计算每个文件大小
            int singleSizeBytes = (int)fs.Length / loopNu;
            int lastFileSizeBytes = (int)fs.Length % loopNu;
            byte[] tempBts = new byte[singleSizeBytes];
            if (lastFileSizeBytes > 0)
            {
                loopNu++;
            }
            for (int idx = 0; idx < loopNu; idx++)
            {
                int readNu = fs.Read(tempBts, 0, tempBts.Length);
                sp.SaveFile(tempBts, readNu, idx);
                ;                //通知完整进度
                CurrentProgress = (float)((idx + 1) * 1.0 / loopNu);
                sp.NotifyEvent(CurrentProgress, 1);
            }
            return true;
        }


    }

    //内存文件分割器
    public class SplitterStateMemory : SplitterState
    {
        public override bool SplitFile(Splitter splitter)
        {
            //0.获取参数信息
            byte[] buffer = (byte[])splitter.StartSpliteFileParam[0];
            int loopNu = (int)splitter.StartSpliteFileParam[1];
            #region 第一种计算方式

            ////1.计算每个文件大小
            //int singleSizeBytes = buffer.Length / loopNu;
            //int lastFileSizeBytes = buffer.Length % loopNu;
            //byte[] tempBts = new byte[singleSizeBytes];
            //if (lastFileSizeBytes > 0)
            //{
            //    loopNu++;
            //}
            //for (int idx = 0; idx < loopNu; idx++)
            //{
            //    int startPos = idx * tempBts.Length;
            //    //2. 判断数据长度是否够array copy
            //    if (startPos + tempBts.Length > buffer.Length)
            //    {
            //        Array.Copy(buffer, startPos, tempBts, 0, lastFileSizeBytes);
            //        splitter.SaveFile(tempBts, lastFileSizeBytes, idx);
            //    }
            //    else
            //    {
            //        Array.Copy(buffer, startPos, tempBts, 0, tempBts.Length);
            //        splitter.SaveFile(tempBts, tempBts.Length, idx);
            //    }
            //    //通知完整进度
            //    CurrentProgress = (float)((idx + 1) * 1.0 / loopNu);
            //    splitter.NotifyEvent(CurrentProgress, 1);
            //}
            //return true;
            #endregion

            MemoryStream ms = new MemoryStream(buffer);
            bool operal = CommonSpliteRAS(loopNu, ms, splitter);
            ms.Close();
            ms.Dispose();
            return operal;
        }

        public override bool CombineFile(Splitter splitter)
        {
            this.CurrentProgress = 0f;
            splitter.NotifyEvent(CurrentProgress, 2);
            //1.获取参数信息
            List<byte[]> splitFiles = (List<byte[]>)splitter.StartSpliteFileParam[0];
            string savePath = splitter.SaveInfo.CompoundPath(string.Empty);
            int curIdx = 1;
            foreach (byte[] itemBuf in splitFiles)
            {
                bool ret = splitter.SaveFile(itemBuf);
                if (!ret)
                {
                    return false;
                }
                this.CurrentProgress = (float)(curIdx * 1.0 / splitFiles.Count);
                splitter.NotifyEvent(CurrentProgress, 2);
                curIdx++;
            }
            return true;
        }

        public override SplitterType CurType => SplitterType.MomoryState;
    }

    //文件路径分割器
    public class SplitterStatePath : SplitterState
    {
        public override SplitterType CurType => SplitterType.FilePathState;

        public override bool CombineFile(Splitter splitter)
        {
            this.CurrentProgress = 0f;
            splitter.NotifyEvent(CurrentProgress, 2);
            //1.获取参数信息
            List<string> splitPath = (List<string>)splitter.StartSpliteFileParam[0];
            string savePath = splitter.SaveInfo.CompoundPath(string.Empty);
            int curIdx = 1;
            foreach (string itemStr in splitPath)
            {
                bool ret = splitter.SaveFile(File.ReadAllBytes(itemStr));
                if (!ret)
                {
                    return false;
                }
                this.CurrentProgress = (float)(curIdx * 1.0 / splitPath.Count);
                splitter.NotifyEvent(CurrentProgress, 2);
                curIdx++;
            }
            return true;
        }

        public override bool SplitFile(Splitter splitter)
        {
            //1.获取参数信息
            string path = (string)splitter.StartSpliteFileParam[0];
            int loopNu = (int)splitter.StartSpliteFileParam[1];
            FileStream fs = File.OpenRead(path);
            bool ret = this.CommonSpliteRAS(loopNu, fs, splitter);
            fs.Close();
            fs.Dispose();
            return ret;
        }
    }

    public enum SplitterType
    {
        MomoryState,
        FilePathState,
    }

    public struct SaveParam
    {
        public string FloderDir { get; set; }
        public string FileName { get; set; }
        public string FileSuffix { get; set; }
        public bool VaildFlag { get; set; }

        public string CompoundPath(string id)
        {
            if (VaildFlag)
            {
                return Path.Combine(FloderDir, string.Format("{0}{1}{2}", FileName, id, FileSuffix));
            }
            else
            {
                return string.Empty;
            }
        }
    }
}
